// 判断是否从该位置起始
String.prototype.startsWith = function (prefix) {
  return prefix && this.length >= prefix.length && this.substring(0, prefix.length) === prefix
}

if (!window.JSON)
  JSON = {}
// 编译
if (typeof JSON.retrocycle !== 'function') {
  JSON.retrocycle = (function () {
    'use strict';

    let t_obj = typeof {},
      t_arr = Object.prototype.toString.apply([]),
      t_str = typeof '';

    // 最终得到绝对xpath地址
    let walk = function (path, _xpath, isArray) {
      if (path.startsWith('$')) // 基于xpath直接定位
        return path;
      else { // 相对回溯定位
        let x, j = path.split('..'),
          k = -j.length + (isArray ? 2 : 1),
          last = j.slice(-1)[0].replace('/', '.');
        x = k < 0 ? _xpath.slice(0, k) : _xpath.slice(0);
        if (last && !last.startsWith('.') && !last.startsWith('['))
          last = `.${last}`
        path = x.join('.') + last;
      }
      return path
    }

    return function ($) {
      let xpath = ['$'];
      (function rez(value) {
        let item, path, _x;
        if (value && typeof value === t_obj) {
          // 如果是数组
          if (Object.prototype.toString.apply(value) === t_arr) {
            for (let i = 0, j = value.length; i < j; i++) {
              item = value[i];
              if (item && typeof item === t_obj) {
                xpath.push(`${xpath.pop()}[${i}]`); // 下标引用要合并分级
                path = item.$ref;
                if (typeof path === t_str)
                  value[i] = eval(walk(path, xpath, true));
                else
                  rez(item);
                if (_x = xpath.pop())
                  xpath.push(_x.slice(0, _x.indexOf('['))); // 下标引用还原分级
              }
            }
          } else {
            // 如果是非数组形式的对象，对其属性做遍历
            for (let name in value) {
              if (value.hasOwnProperty(name) && typeof value[name] === t_obj) {
                xpath.push(name);
                item = value[name];
                if (item) {
                  path = item.$ref;
                  if (typeof path === t_str)
                    value[name] = eval(walk(path, xpath));
                  else
                    rez(item);
                }
                xpath.pop();
              }
            }
          }
        }
      })($)
      return $
    }
  })()
}

// 解析
if (typeof JSON.decycle !== "function") {
  JSON.decycle = function decycle(object, replacer, isNotDot) {
    // 制作一个对象或数组的深层副本，确保最多存在
    // 结果结构中每个对象或数组的一个实例。该
    // 重复引用（可能正在形成循环）被替换为
    // 如下的对象：
    //  {“$ ref”：PATH}
    // 其中PATH是一个定位第一次出现的JSONPath字符串。

    //如果提供了replacer函数，那么将为每个值调用它。
    // replacer函数接收一个值并返回一个替换值。

    // JSONPath用于定位唯一对象。$表示最高级别
    // 对象或数组。([NUMBER],.NUMBER)或[STRING]表示子元素或属性
    'use strict';
    // 使用WeackMap构建只接受对象作为键名的对象（对象路径映射）
    let objects = new WeakMap();

    return (function derez(value, path) {
      // derez函数通过对象进行递归，生成深层副本。
      let old_path, newJson
      // 如果提供了替代功能，则调用它以获取替换值。
      if (replacer !== undefined) {
        value = replacer(value)
      }
      // 判断value是不是object，如果是object将其转换为路径引用
      if (typeof value === "object" &&
        value !== null &&
        !(value instanceof Boolean) &&
        !(value instanceof Date) &&
        !(value instanceof Number) &&
        !(value instanceof RegExp) &&
        !(value instanceof String)
      ) {

        // 如果值是对象或数组，查看是否已经存在
        // 如果存在，返回{“$ ref”：PATH}对象。这用了
        //  ES6 WeakMap
        old_path = objects.get(value)
        if (old_path !== undefined) {
          return {
            $ref: old_path
          }
        }
        // 否则，累积唯一值及其路径。
        objects.set(value, path)

        if (Array.isArray(value)) {
          //如果是数组，则复制数组。
          newJson = []
          value.forEach((element, i) => {
            newJson[i] = derez(element, `${path}[${i}]`)
          })
        } else {
          //如果是对象，则复制该对象。
          newJson = {}
          Object.keys(value).forEach((name, index) => {
            newJson[name] = derez(
              value[name],
              isNotDot ? `${path}[${JSON.stringify(name)}]` : `${path}${Object.keys(value).length === index ? '' : '.'}${name}`
            )
          })
        }
        return newJson
      }
      return value
    }(object, '$'))
  }
}